home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Graphics Programming (2nd Edition) / Visual Basic Graphics Programming 2nd Edition.iso / Src / Ch17 / SimpPgon.cls < prev    next >
Text File  |  1999-07-04  |  12KB  |  425 lines

  1. VERSION 1.0 CLASS
  2. BEGIN
  3.   MultiUse = -1  'True
  4.   Persistable = 0  'NotPersistable
  5.   DataBindingBehavior = 0  'vbNone
  6.   DataSourceBehavior  = 0  'vbNone
  7.   MTSTransactionMode  = 0  'NotAnMTSObject
  8. END
  9. Attribute VB_Name = "SimplePolygon"
  10. Attribute VB_GlobalNameSpace = False
  11. Attribute VB_Creatable = True
  12. Attribute VB_PredeclaredId = False
  13. Attribute VB_Exposed = False
  14. Option Explicit
  15.  
  16. ' A simple polygon class.
  17.  
  18. Private Type POINTAPI
  19.     X As Long
  20.     Y As Long
  21. End Type
  22. Private Declare Function Polygon Lib "gdi32" (ByVal hdc As Long, lpPoint As POINTAPI, ByVal nCount As Long) As Long
  23.  
  24. Public ForeColor As Long
  25. Public FillColor As Long
  26.  
  27. Public PointX As Collection
  28. Public PointY As Collection
  29. Public PointZ As Collection
  30.  
  31. ' Normal vector.
  32. Public Nx As Single
  33. Public Ny As Single
  34. Public Nz As Single
  35.  
  36. ' Bounding box.
  37. Public Xmin As Single
  38. Public Xmax As Single
  39. Public Ymin As Single
  40. Public Ymax As Single
  41. Public Zmin As Single
  42. Public Zmax As Single
  43.  
  44. ' Compare this polygon to pgon. If it is below us,
  45. ' return -1. If it is above us, return 1. If it is
  46. ' neither above nor below, return 0.
  47. Public Function CompareToSimplePolygon(ByVal pgon As SimplePolygon) As Integer
  48. Dim i As Integer
  49. Dim Cx As Single
  50. Dim Cy As Single
  51. Dim Cz As Single
  52. Dim Vx As Single
  53. Dim Vy As Single
  54. Dim Vz As Single
  55. Dim old_sign As Integer
  56. Dim new_sign As Integer
  57. Dim dot_product As Single
  58. Dim same_side As Boolean
  59.  
  60.     ' Get a point on our polygon.
  61.     Cx = PointX(1)
  62.     Cy = PointY(1)
  63.     Cz = PointZ(1)
  64.  
  65.     ' See if pgon lies on one side or the other.
  66.     With pgon
  67.         ' Assume we will succeed.
  68.         same_side = True
  69.  
  70.         old_sign = 0
  71.         For i = 1 To pgon.PointX.Count
  72.             ' Get the vector to this point.
  73.             Vx = .PointX(i) - Cx
  74.             Vy = .PointY(i) - Cy
  75.             Vz = .PointZ(i) - Cz
  76.  
  77.             ' Get the dot product.
  78.             dot_product = Vx * Nx + Vy * Ny + Vz * Nz
  79.  
  80.             ' See if the dot_product is too
  81.             ' small to be useful.
  82.             If Abs(dot_product) > 0.01 Then
  83.                 ' dot_product is big enough to use.
  84.                 ' Get the dot product's sign.
  85.                 new_sign = Sgn(dot_product)
  86.  
  87.                 ' See if this matches the current sign.
  88.                 If old_sign = 0 Then
  89.                     old_sign = new_sign
  90.                 ElseIf old_sign <> new_sign Then
  91.                     same_side = False
  92.                     Exit For
  93.                 End If
  94.             End If
  95.         Next i
  96.     End With
  97.  
  98.     ' See if we got a result.
  99.     If same_side Then
  100.         ' We got a result. See which side pgon is on.
  101.         If (old_sign < 0) Then
  102.             ' It's below.
  103.             CompareToSimplePolygon = -1
  104.         Else
  105.             ' It's above.
  106.             CompareToSimplePolygon = 1
  107.         End If
  108.     Else
  109.         CompareToSimplePolygon = 0
  110.     End If
  111. End Function
  112.  
  113.  
  114. ' Add a point to the polygon, skipping duplicates.
  115. Public Sub AddPoint(ByVal X As Single, ByVal Y As Single, ByVal Z As Single)
  116. Dim num_points As Integer
  117.  
  118.     If PointX Is Nothing Then
  119.         ' Allocate the coordinate collections.
  120.         Set PointX = New Collection
  121.         Set PointY = New Collection
  122.         Set PointZ = New Collection
  123.     Else
  124.         ' If this is a duplicate point, skip it.
  125.         num_points = PointX.Count
  126.         If (Abs(PointX(num_points) - X) < 0.001) And _
  127.            (Abs(PointY(num_points) - Y) < 0.001) And _
  128.            (Abs(PointZ(num_points) - Z) < 0.001) _
  129.                 Then Exit Sub
  130.     End If
  131.  
  132.     ' Add the new point.
  133.     PointX.Add X
  134.     PointY.Add Y
  135.     PointZ.Add Z
  136. End Sub
  137. ' Draw the polygon.
  138. Public Sub DrawPolygon(ByVal pic As PictureBox)
  139. Dim num_points As Long
  140. Dim pts() As POINTAPI
  141. Dim i As Integer
  142. Dim light_source As LightSource
  143.  
  144.     ' Load the points array.
  145.     num_points = PointX.Count
  146.     ReDim pts(1 To num_points)
  147.     For i = 1 To num_points
  148.         With pts(i)
  149.             .X = PointX(i)
  150.             .Y = PointY(i)
  151.         End With
  152.     Next i
  153.  
  154.     pic.ForeColor = ForeColor
  155.     pic.FillColor = FillColor
  156.  
  157.     ' Draw the polygon.
  158.     Polygon pic.hdc, pts(1), num_points
  159. End Sub
  160. ' Return True if this is a backface.
  161. Public Function IsBackface() As Boolean
  162.     ' After the transformation (which includes
  163.     ' the projection transformation), the viewing
  164.     ' vector is <0, 0, -EyeR>. Then N dot V is
  165.     ' nx * 0 + ny * 0 + nz * (-EyeR) = -nz * EyeR.
  166.     ' The face a backface if dot product >= 0.
  167.     ' That happens is nz <= 0.
  168.     IsBackface = (Nz <= 0)
  169. End Function
  170. ' Prepare for sorting the polygon with others.
  171. Public Sub Finish()
  172. Dim i As Integer
  173. Dim dist As Single
  174.  
  175.     ' Get the bounding box.
  176.     SetBoundingBox
  177.  
  178.     ' Get the normal vector.
  179.     If PointX.Count < 3 Then
  180.         Nx = 0
  181.         Ny = 0
  182.         Nz = 0
  183.     Else
  184.         ' Get the normal.
  185.         m3Cross Nx, Ny, Nz, _
  186.             PointX(2) - PointX(1), PointY(2) - PointY(1), PointZ(2) - PointZ(1), _
  187.             PointX(3) - PointX(2), PointY(3) - PointY(2), PointZ(3) - PointZ(2)
  188.         dist = Sqr(Nx * Nx + Ny * Ny + Nz * Nz)
  189.         Nx = Nx / dist
  190.         Ny = Ny / dist
  191.         Nz = Nz / dist
  192.     End If
  193. End Sub
  194. ' Set the bounding box.
  195. Public Sub SetBoundingBox()
  196. Dim i As Integer
  197.  
  198.     ' Get the bounding box.
  199.     Xmin = PointX(1): Xmax = Xmin
  200.     Ymin = PointY(1): Ymax = Ymin
  201.     Zmin = PointZ(1): Zmax = Zmin
  202.     For i = 2 To PointX.Count
  203.         If Xmin > PointX(i) Then Xmin = PointX(i)
  204.         If Xmax < PointX(i) Then Xmax = PointX(i)
  205.         If Ymin > PointX(i) Then Ymin = PointX(i)
  206.         If Ymax < PointX(i) Then Ymax = PointX(i)
  207.         If Zmin > PointX(i) Then Zmin = PointX(i)
  208.         If Zmax < PointX(i) Then Zmax = PointX(i)
  209.     Next i
  210. End Sub
  211.  
  212. ' Return true if this polygon is completly above
  213. ' the plane containing pgon.
  214. Public Function IsAbove(pgon As SimplePolygon) As Boolean
  215.     IsAbove = (pgon.CompareToSimplePolygon(Me) > 0)
  216. End Function
  217. ' Return true if this polygon is completly below
  218. ' the plane containing pgon.
  219. Public Function IsBelow(pgon As SimplePolygon) As Boolean
  220.     IsBelow = (pgon.CompareToSimplePolygon(Me) < 0)
  221. End Function
  222.  
  223.  
  224. ' Return true if this polygon obscures pgon.
  225. '
  226. ' 1. Check X and Y extents.
  227. ' 2. See if we are below the plane of pgon.
  228. ' 3. See if pgon is above our plane.
  229. ' 4. See where the projections of the edges of
  230. '    the polygons cross. Where they cross, see
  231. '    if one Z value is greater than the other.
  232. ' 5. See if one polygon contains the other.
  233. Public Function Obscures(pgon As SimplePolygon) As Boolean
  234. Dim num_i As Integer
  235. Dim num_j As Integer
  236. Dim i As Integer
  237. Dim j As Integer
  238. Dim xi1 As Single
  239. Dim yi1 As Single
  240. Dim zi1 As Single
  241. Dim xi2 As Single
  242. Dim yi2 As Single
  243. Dim zi2 As Single
  244. Dim xj1 As Single
  245. Dim yj1 As Single
  246. Dim zj1 As Single
  247. Dim xj2 As Single
  248. Dim yj2 As Single
  249. Dim zj2 As Single
  250. Dim X As Single
  251. Dim Y As Single
  252. Dim z1 As Single
  253. Dim z2 As Single
  254.  
  255.     ' 1. Check X and Y extents.
  256.     Obscures = False
  257.     If Xmin >= pgon.Xmax Then Exit Function
  258.     If Xmax <= pgon.Xmin Then Exit Function
  259.     If Ymin >= pgon.Ymax Then Exit Function
  260.     If Ymax <= pgon.Ymin Then Exit Function
  261.  
  262.     ' 2. See if we are below the plane of pgon.
  263.     If IsBelow(pgon) Then Exit Function
  264.  
  265.     ' 3. See if pgon is above our plane.
  266.     If pgon.IsAbove(Me) Then Exit Function
  267.  
  268.     ' 4. See where the projections of the edges of
  269.     '    the polygons cross. Where they cross, see
  270.     '    if one Z value is greater than the other.
  271.     num_i = PointX.Count
  272.  
  273.     ' Check each edge in this polygon.
  274.     xi1 = PointX(num_i)
  275.     yi1 = PointY(num_i)
  276.     zi1 = PointZ(num_i)
  277.     For i = 1 To num_i
  278.         xi2 = PointX(i)
  279.         yi2 = PointY(i)
  280.         zi2 = PointZ(i)
  281.  
  282.         ' Compare the i1-i2 edge with each edge
  283.         ' in pgon.
  284.         num_j = pgon.PointX.Count
  285.         xj1 = pgon.PointX(num_j)
  286.         yj1 = pgon.PointY(num_j)
  287.         zj1 = pgon.PointZ(num_j)
  288.         For j = 1 To num_j
  289.             xj2 = pgon.PointX(j)
  290.             yj2 = pgon.PointY(j)
  291.             zj2 = pgon.PointZ(j)
  292.  
  293.             ' See if the segments cross.
  294.             If FindCrossing( _
  295.                 xi1, yi1, zi1, _
  296.                 xi2, yi2, zi2, _
  297.                 xj1, yj1, zj1, _
  298.                 xj2, yj2, zj2, _
  299.                 X, Y, z1, z2) _
  300.             Then
  301.                 If z1 - z2 > 0.01 Then
  302.                     ' z1 > z2. We obscure pgon.
  303.                     Obscures = True
  304.                     Exit Function
  305.                 End If
  306.                 If z2 - z1 > 0.01 Then
  307.                     ' z2 > z1. pgon obscures us.
  308.                     Obscures = False
  309.                     Exit Function
  310.                 End If
  311.             End If
  312.  
  313.             xj1 = xj2
  314.             yj1 = yj2
  315.             zj1 = zj2
  316.         Next j
  317.  
  318.         xi1 = xi2
  319.         yi1 = yi2
  320.         zi1 = zi2
  321.     Next i
  322.  
  323.     ' No edges cross. See if one polygon contains
  324.     ' the other.
  325.     '
  326.     ' If any points of one polygon are inside the
  327.     ' other, then they must all be. Since the
  328.     ' IsAbove tests were inconclusive, some points
  329.     ' in one polygon are on the "bad" side of the
  330.     ' other. In that case, we obecure pgon.
  331.     '
  332.     ' See if this polygon is inside the other.
  333.     xi1 = PointX(1)
  334.     yi1 = PointY(1)
  335.     If pgon.PointInside(xi1, yi1) Then
  336.         Obscures = True
  337.         Exit Function
  338.     End If
  339.  
  340.     ' See if the other polygon is inside this one.
  341.     xi1 = pgon.PointX(1)
  342.     yi1 = pgon.PointY(1)
  343.     If PointInside(xi1, yi1) Then
  344.         Obscures = True
  345.         Exit Function
  346.     End If
  347.  
  348.     Obscures = False
  349. End Function
  350. ' Return true if the point's projection lies within
  351. ' this polygon's projection.
  352. Function PointInside(ByVal X As Single, ByVal Y As Single) As Boolean
  353. Dim i As Integer
  354. Dim theta1 As Double
  355. Dim theta2 As Double
  356. Dim dtheta As Double
  357. Dim dx As Double
  358. Dim dy As Double
  359. Dim angles As Double
  360.  
  361.     dx = PointX(PointX.Count) - X
  362.     dy = PointY(PointY.Count) - Y
  363.     theta1 = ATan2(CSng(dx), CSng(dy))
  364.     If theta1 < 0 Then theta1 = theta1 + 2 * PI
  365.     For i = 1 To PointX.Count
  366.         dx = PointX(i) - X
  367.         dy = PointY(i) - Y
  368.         theta2 = ATan2(CSng(dx), CSng(dy))
  369.         If theta2 < 0 Then theta2 = theta2 + 2 * PI
  370.         dtheta = theta2 - theta1
  371.         If dtheta > PI Then dtheta = dtheta - 2 * PI
  372.         If dtheta < -PI Then dtheta = dtheta + 2 * PI
  373.         angles = angles + dtheta
  374.         theta1 = theta2
  375.     Next i
  376.  
  377.     PointInside = (Abs(angles) > 0.001)
  378. End Function
  379. ' See where the projections of two segments cross.
  380. ' Return true if the segments cross, false
  381. ' otherwise.
  382. Function FindCrossing( _
  383.     ax1 As Single, ay1 As Single, az1 As Single, _
  384.     ax2 As Single, ay2 As Single, az2 As Single, _
  385.     bx1 As Single, by1 As Single, bz1 As Single, _
  386.     bx2 As Single, by2 As Single, bz2 As Single, _
  387.     X As Single, Y As Single, z1 As Single, z2 As Single) _
  388.         As Boolean
  389. Dim dxa As Single
  390. Dim dya As Single
  391. Dim dza As Single
  392. Dim dxb As Single
  393. Dim dyb As Single
  394. Dim dzb As Single
  395. Dim t1 As Single
  396. Dim t2 As Single
  397. Dim denom As Single
  398.  
  399.     dxa = ax2 - ax1
  400.     dya = ay2 - ay1
  401.     dxb = bx2 - bx1
  402.     dyb = by2 - by1
  403.     
  404.     FindCrossing = False
  405.     
  406.     denom = dxb * dya - dyb * dxa
  407.     ' If the segments are parallel, stop.
  408.     If denom < 0.01 And denom > -0.01 Then Exit Function
  409.  
  410.     t2 = (ax1 * dya - ay1 * dxa - bx1 * dya + by1 * dxa) / denom
  411.     If t2 < 0 Or t2 > 1 Then Exit Function
  412.     
  413.     t1 = (ax1 * dyb - ay1 * dxb - bx1 * dyb + by1 * dxb) / denom
  414.     If t1 < 0 Or t1 > 1 Then Exit Function
  415.  
  416.     ' Compute the points of overlap.
  417.     X = ax1 + t1 * dxa
  418.     Y = ay1 + t1 * dya
  419.     dza = az2 - az1
  420.     dzb = bz2 - bz1
  421.     z1 = az1 + t1 * dza
  422.     z2 = bz1 + t2 * dzb
  423.     FindCrossing = True
  424. End Function
  425.